package ga;

import java.io.IOException;
import java.io.InputStream;

public class AnalizadorLexico 
{
	enum EstadoLexico
	{
		INICIO, RECAMPERSAND, RECRESTA, RECFLECHA, RECSUMA, RECMULT,
		RECDIV, RECCOMENTARIO, RECCORCHETEA, RECCORCHETEC, RECPYC, RECDOSPUNTOS, RECSIMB, RECIGUAL,
		RECNUM, RECAPARENTESIS, RECCPARENTESIS, RECEOF,
		
		RECCCORCHETE,RECCOMA,RECPUNTO,RECACCPUNTERO,
		
		ALLAVE,CLLAVE
		
	};
	
	private final static int EOF = -1;
	private InputStream f;
	private int col;
	private int fil;
	private int filIni;
    private int colIni;
	private String lexema;
	private int caracter;
	private EstadoLexico estado;
	
	public AnalizadorLexico(InputStream f) throws IOException
	{
		this.f = f;
		col = -1;
		fil = 0;
		caracter = sigCaracter();
	}
	
	private int sigCaracter() throws IOException
	{
		caracter = f.read();
		if(caracter == '\n')
		{
			col = -1;
			fil++;
		}
		else
			col++;
		
		return caracter;
	}
	
	public Token sigToken() throws IOException 
	{
        estado = EstadoLexico.INICIO;
        filIni = fil;
        colIni = col;
        lexema = "";
        while (true) 
		{
            switch (estado) 
			{
			/*
			INCIO, 
			//RECAMPERSAND, 
			//RECRESTA, 
			//RECFLECHA, 
			//RECSUMA, 
			//RECMULT,
			//RECDIV, 
			//RECCOMENTARIO, 
			//RECCORCHETEA, 
			//RECCORCHETEC,
			//RECPYC, 
			//RECSIMB, 
			//RECDOSPUNTOS,
			//RECIGUAL,
			//RECNUM, 
			//RECAPARENTESIS, 
			//RECCPARENTESIS, 
			//RECEOF
		*/
                case INICIO:
                    if (caracter == '&')
					{
                        transita(EstadoLexico.RECAMPERSAND);
                    } else if (caracter == '-') 
					{
                        transita(EstadoLexico.RECRESTA);
					} else if (esDigito(caracter)) 
					{
                        transita(EstadoLexico.RECNUM);
                    } else if (caracter == '+') 
					{
                        transita(EstadoLexico.RECSUMA);
                    } else if (caracter == '*') 
					{
                        transita(EstadoLexico.RECMULT);
                    } else if (caracter == '/') 
					{
                        transita(EstadoLexico.RECDIV);
                    } else if (caracter == EOF) 
					{
                        transita(EstadoLexico.RECEOF);
                    } else if (caracter == '[') 
					{
                        transita(EstadoLexico.RECCORCHETEA);
					} else if (caracter == ';') 
					{
						transita(EstadoLexico.RECPYC);
					} else if (caracter == ':') 
					{
						transita(EstadoLexico.RECDOSPUNTOS);
					} else if (caracter == '_' || esLetra(caracter))
					{
						transita(EstadoLexico.RECSIMB);
					} else if (caracter == '(') 
					{
						transita(EstadoLexico.RECAPARENTESIS);
					} else if (caracter == ')') 
					{
						transita(EstadoLexico.RECCPARENTESIS);	
					} else if (caracter == ']') 
					{
                        transita(EstadoLexico.RECCCORCHETE);
                    } else if (caracter == '.') 
					{
                        transita(EstadoLexico.RECPUNTO);
                    } else if (caracter == ',') 
					{
                        transita(EstadoLexico.RECCOMA);
                    } else if (caracter == '^') 
					{
                        transita(EstadoLexico.RECACCPUNTERO);
					} else if (caracter == '@')
					{
						transitaIgnorando(EstadoLexico.RECCOMENTARIO);
                    }else if (esSep(caracter)) 
					{
                        transitaIgnorando(EstadoLexico.INICIO);
                    } else if (caracter == '{') 
					{
                        transitaIgnorando(EstadoLexico.ALLAVE);
                    } else if (caracter == '}') 
					{
                        transitaIgnorando(EstadoLexico.CLLAVE);
                    }
                    else {
                        errorLexico();
                    }
                    break;
                case RECAMPERSAND:
                    return new Token(filIni,colIni,CatLexica.AMPERSAND);
                case RECAPARENTESIS:
                	return new Token(filIni,colIni,CatLexica.APARENTESIS);
                case RECCPARENTESIS:
                	return new Token(filIni,colIni,CatLexica.CPARENTESIS);
                case RECRESTA:
					if(caracter == '>')
						transita(EstadoLexico.RECFLECHA);
					else return new Token(filIni,colIni,CatLexica.RESTA);
					break;
				case RECFLECHA:
					return new Token(filIni,colIni,CatLexica.FLECHA);
                case RECSUMA:
                    return new Token(filIni,colIni,CatLexica.SUMA);
				case RECMULT:
                    return new Token(filIni,colIni,CatLexica.MULT);
				case RECDIV:
                    return new Token(filIni,colIni,CatLexica.DIV);
				case RECCORCHETEA:
					if(caracter == ']')
						transita(EstadoLexico.RECCORCHETEC);
					else 
						return new Token(filIni,colIni,CatLexica.ACORCHETE);
					break;
				case RECCORCHETEC:
                    return new Token(filIni,colIni,CatLexica.CORCHETES);
				case RECPYC:
                    return new Token(filIni,colIni,CatLexica.PYC);
				case RECDOSPUNTOS:
                    if(caracter == '=')
						transita(EstadoLexico.RECIGUAL);						
					else errorLexico();
					break;
				case RECIGUAL:
					return new Token(filIni,colIni,CatLexica.ASIG);               
                case RECNUM:
                    if (esDigito(caracter)) {
                        transita(EstadoLexico.RECNUM);
                    } else {
                        return new Token(filIni,colIni,CatLexica.NUM, lexema);
                    }
                    break;
                case RECSIMB:
                	if (esLetra(caracter) || esDigito(caracter) || caracter == '_')
                	{
                		transita(EstadoLexico.RECSIMB);
                	} else {
                		return tokenSimbolo();
                	}
                	break;
                case RECEOF:
                    return new Token(filIni,colIni,CatLexica.EOF);
                case RECCOMENTARIO:
                    if (caracter != '\n' && caracter != EOF) {
                        transitaIgnorando(EstadoLexico.RECCOMENTARIO);
                    } else {
                        transitaIgnorando(EstadoLexico.INICIO);
                    }
                    break;
                case RECCCORCHETE:
                    return new Token(filIni,colIni,CatLexica.CCORCHETE);    
                case RECCOMA:
                    return new Token(filIni,colIni,CatLexica.COMA);    
                case RECPUNTO:
                    return new Token(filIni,colIni,CatLexica.PUNTO);    
                case RECACCPUNTERO:
                    return new Token(filIni,colIni,CatLexica.ACCPUNTERO);  
                case ALLAVE:
                	return new Token(filIni,colIni,CatLexica.ALLAVE);
                case CLLAVE:
                	return new Token(filIni,colIni,CatLexica.CLLAVE);
            }
        }
    }
	
	private void transita(EstadoLexico aEstado) throws IOException 
	{
        lexema = lexema + (char) caracter;
        transitaIgnorando(aEstado);
    }

    private void transitaIgnorando(EstadoLexico aEstado) throws IOException 
	{
        estado = aEstado;
        filIni = fil;
        colIni = col;
        caracter = sigCar();
    }

    private int sigCar() throws IOException 
	{
        caracter = f.read();
        if (caracter == '\n') {
            col = -1;
            fil++;
        } else {
            col++;
        }
        return caracter;
    }

    private boolean esLetra(int car) {
        return (car >= 'a' && car <= 'z') || (car >= 'A' && car <= 'Z');
    }

    private boolean esDigito(int car) {
        return (car >= '0' && car <= '9');
    }

    private boolean esSep(int car) {
        return car == ' ' || car == '\t' || car == '\n' || car == '\r' || car == '\b';
    }

    private void errorLexico() {
        System.err.println("(" + fil + "," + col + ")" + 
                "ERROR LEXICO: caracter desconocido:" + (char) caracter);
        System.exit(1);
    }

    ;
   private Token tokenSimbolo() {
        if (lexema.equals("true"))
            return new Token(filIni,colIni,CatLexica.TRUE);
        else if (lexema.equals("false")) 
            return new Token(filIni,colIni,CatLexica.FALSE);
        else if (lexema.equals("and")) 
            return new Token(filIni,colIni,CatLexica.AND);
        else if (lexema.equals("or")) 
            return new Token(filIni,colIni,CatLexica.OR);
        else if (lexema.equals("not")) 
            return new Token(filIni,colIni,CatLexica.NOT);
        else if (lexema.equals("eq")) 
            return new Token(filIni,colIni,CatLexica.EQ);
        else if (lexema.equals("neq")) 
            return new Token(filIni,colIni,CatLexica.NEQ);
        else if (lexema.equals("lt")) 
            return new Token(filIni,colIni,CatLexica.LT);
        else if (lexema.equals("le")) 
            return new Token(filIni,colIni,CatLexica.LE);
        else if (lexema.equals("gt")) 
            return new Token(filIni,colIni,CatLexica.GT);
        else if (lexema.equals("ge")) 
            return new Token(filIni,colIni,CatLexica.GE);
        else if (lexema.equals("if")) 
            return new Token(filIni,colIni,CatLexica.IF);
        else if (lexema.equals("fi")) 
            return new Token(filIni,colIni,CatLexica.FI);
        else if (lexema.equals("do")) 
            return new Token(filIni,colIni,CatLexica.DO);
        else if (lexema.equals("od")) 
            return new Token(filIni,colIni,CatLexica.OD);
        else if (lexema.equals("case")) 
            return new Token(filIni,colIni,CatLexica.CASE);
        else if (lexema.equals("int")) 
            return new Token(filIni,colIni,CatLexica.INT);
        else if (lexema.equals("boolean")) 
            return new Token(filIni,colIni,CatLexica.BOOLEAN);
        else if (lexema.equals("proc")) 
            return new Token(filIni,colIni,CatLexica.PROC);
        else if (lexema.equals("var")) 
            return new Token(filIni,colIni,CatLexica.VAR);
        else if (lexema.equals("tipo")) 
            return new Token(filIni,colIni,CatLexica.TIPO);
        else if (lexema.equals("de")) 
            return new Token(filIni,colIni,CatLexica.DE);
        else if (lexema.equals("tabla")) 
            return new Token(filIni,colIni,CatLexica.TABLA);
        else if (lexema.equals("registro")) 
            return new Token(filIni,colIni,CatLexica.REGISTRO);
        else if (lexema.equals("puntero")) 
            return new Token(filIni,colIni,CatLexica.PUNTERO);
        else if (lexema.equals("new")) 
            return new Token(filIni,colIni,CatLexica.NEW);
        else if (lexema.equals("dispose")) 
            return new Token(filIni,colIni,CatLexica.DELETE);
        else if (lexema.equals("leer")) 
            return new Token(filIni,colIni,CatLexica.LEER);
        else if (lexema.equals("escribir")) 
            return new Token(filIni,colIni,CatLexica.ESCRIBIR);
        
        else
            return new Token(filIni,colIni,CatLexica.IDENT, lexema);
    }

}
